package grpc

import (
	"context"
	"e-commerce/services/inventory-service/internal/service"
	"log"
	"os"

	authpb "e-commerce/services/auth-service/api/pb"
	inventorypb "e-commerce/services/inventory-service/api/pb"

	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"gopkg.in/yaml.v3"
)

type AuthClient struct {
	client authpb.AuthServiceClient
	conn   *grpc.ClientConn
}

type Config struct {
	AuthService auth_service `yaml:"auth_service"`
}

type auth_service struct {
	Addr string `yaml:"addr"`
}

func NewAuthClient(configPath string) *AuthClient {
	var config Config
	yamlFile, err := os.ReadFile(configPath)
	if err != nil {
		log.Fatalf("Failed to read config file: %v", err)
	}
	err = yaml.Unmarshal(yamlFile, &config)
	if err != nil {
		log.Fatalf("Failed to unmarshal config file: %v", err)
	}
	conn, err := grpc.NewClient(
		config.AuthService.Addr,
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
	)
	if err != nil {
		log.Fatalf("Failed to connect to auth service: %v", err)
	}
	return &AuthClient{
		client: authpb.NewAuthServiceClient(conn),
		conn:   conn,
	}
}

type InventoryServiceServer struct {
	inventorypb.UnimplementedInventoryServiceServer
	service    *service.InventoryService
	authclient *AuthClient
}

func NewInventoryServiceServer(configPath string) *InventoryServiceServer {
	service := service.NewInventoryService(configPath)
	authclient := NewAuthClient(configPath)
	return &InventoryServiceServer{service: service, authclient: authclient}
}

func (s *InventoryServiceServer) NewOrderQueue() error {
	return s.service.NewOrderQueue()
}

func (s *InventoryServiceServer) ReserveStock(ctx context.Context, req *inventorypb.ReserveStockRequest) (*inventorypb.ReserveStockResponse, error) {
	err := s.service.ReserveStock(req.ProductId, req.Quantity)
	if err != nil {
		return &inventorypb.ReserveStockResponse{Success: false}, err
	}
	return &inventorypb.ReserveStockResponse{Success: true}, nil
}

func (s *InventoryServiceServer) ReleaseStock(ctx context.Context, req *inventorypb.ReleaseStockRequest) (*inventorypb.ReleaseStockResponse, error) {
	err := s.service.ReleaseStock(ctx, req.OrderId)
	if err != nil {
		return &inventorypb.ReleaseStockResponse{Success: false}, err
	}
	return &inventorypb.ReleaseStockResponse{Success: true}, nil
}

func (s *InventoryServiceServer) QueryStock(ctx context.Context, req *inventorypb.QueryStockRequest) (*inventorypb.QueryStockResponse, error) {
	stock, err := s.service.QueryStock(req.ProductId)
	if err != nil {
		return &inventorypb.QueryStockResponse{Success: false}, err
	}
	return &inventorypb.QueryStockResponse{Success: true, Quantity: stock}, nil
}

type contextKey string

const (
	userIDKey contextKey = "user_id"
	roleKey   contextKey = "role"
)

// validatetoken中间件
func (s *InventoryServiceServer) AuthMiddleware(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	token := metadata.ValueFromIncomingContext(ctx, "authorization")
	if len(token) == 0 {
		return nil, status.Error(codes.Unauthenticated, "no token provided")
	}
	response, err := s.authclient.client.ValidateToken(ctx, &authpb.ValidateTokenRequest{Token: token[0]})
	if err != nil {
		return nil, status.Error(codes.Unauthenticated, "invalid token,err:"+err.Error())
	}
	if !response.Valid {
		return nil, status.Error(codes.Unauthenticated, "invalid token")
	}
	newCtx := context.WithValue(ctx, userIDKey, response.UserId)
	newCtx = context.WithValue(newCtx, roleKey, response.Role)
	return handler(newCtx, req)
}

func (s *InventoryServiceServer) ReservedConsume() error {
	return s.service.ReservedConsume()
}

func (s *InventoryServiceServer) RollbackConsume() error {
	return s.service.RollbackConsume()
}
